<!DOCTYPE html>
            
<HTML>
<HEAD>
<meta name="booktitle" content="Developing Applications With Objective Caml" >
 <meta charset="ISO-8859-1"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<META name="GENERATOR" content="hevea 1.05-7 of 2000-02-24">
<META NAME="Author" CONTENT="Christian.Queinnec@lip6.fr">
<LINK rel=stylesheet type="text/css" href="videoc-ocda.css">
<script language="JavaScript" src="videoc.js"><!--
//--></script>
<TITLE>
 Client-server
</TITLE>
</HEAD>
<BODY class="regularBody">
<A HREF="book-ora186.html"><IMG SRC ="previous_motif.gif" ALT="Previous"></A>
<A HREF="index.html"><IMG SRC ="contents_motif.gif" ALT="Contents"></A>
<A HREF="book-ora188.html"><IMG SRC ="next_motif.gif" ALT="Next"></A>
<HR>

<H2> Client-server</H2>
<A NAME="@concepts359"></A>
Interprocess communication between processes on the same machine 
or on different machines through TCP/IP sockets 
is a mode of point-to-point asynchronous communication.
The reliability of such transmissions is assured by the 
TCP protocol. It is nonetheless possible to simulate 
the broadcast to a group of processes through 
point-to-point communication to all receivers.<BR>
<BR>
The roles of different processes communicating in an application are 
asymmetric, as a general rule.
That description holds for client-server architectures.
A server is a process (or several processes) accepting 
requests and trying to respond to them. The client, itself
a process, sends a request to the server, hoping for a response.<BR>
<BR>
<A NAME="toc275"></A>
<H3> Client-server Action Model</H3>
A server provides a service on a given port 
by waiting for connections from future clients. 
Figure&nbsp;<A HREF="book-ora187.html#fig-pd2">20.1</A> shows the sequence of principal tasks 
for a server and a client.<BR>
<BR>
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora080.gif">
</DIV>
<BR>
<DIV ALIGN=center>Figure 20.1: Model of a server and client</DIV><BR>

<A NAME="fig-pd2"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE>
A client can connect to a service once the server is ready to 
accept connections (<TT>accept</TT>). 
In order to make a connection, the client must know the 
IP number of the server machine and the port number 
of the service. If the client does not know the IP number, 
it needs to request name/number resolution using the function
<TT>gethostbyname</TT>. 
Once the connection is accepted by the server, each program can
communicate via input-output channels over the sockets created at both ends.<BR>
<BR>
<A NAME="toc276"></A>
<H3> Client-server Programming</H3>
The mechanics of client-server programming
follows the model described in Figure&nbsp;<A HREF="book-ora187.html#fig-pd2">20.1</A>. 
These tasks are always performed.
For these tasks, we write generic functions parameterized 
by particular functions for a given server.
As an example of such a program, we describe a server that 
accepts a connection from a client, waits on a socket 
until a line of text has been received, converting the line to CAPITALS,
and sending back the converted text to the client.<BR>
<BR>
Figure&nbsp;<A HREF="book-ora187.html#fig-pdmaj">20.2</A> shows the communication between the 
service and different clients<A NAME="text47" HREF="book-ora192.html#note47"><SUP><FONT SIZE=2>1</FONT></SUP></A>.<BR>
<BR>
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora081.gif">
</DIV>
<BR>
<DIV ALIGN=center>Figure 20.2: CAPITAL service and its clients</DIV><BR>

<A NAME="fig-pdmaj"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE>Certain tasks run on the same machine as the server, while others are 
found on remote machines.<BR>
<BR>
We will see 
<OL type=1>
<LI>
 How to write the code for a ``generic server'' and 
instantiate it for our particular capitalization service. 

<LI> How to test the server, without writing the client, by 
using the <TT>telnet</TT> program.

<LI> How to create two types of clients:<BR><UL>
<LI>

a sequential client, which waits for a response after sending a request;<BR>
<BR>

<LI>
a parallel client, which separates the send and receive tasks.
</UL>
Therefore, there are two processes for this client.
</OL><A NAME="toc277"></A>
<H3> Code for the Server</H3><A NAME="subsec-code-serveur"></A>A server may be divided into two parts: waiting for a connection
and the following code to handle the connection.<BR>
<BR>

<H4> A Generic Server</H4>
The generic server function <TT>establish_server</TT> described below 
takes as its first argument a function
for the service (<TT>server_fun</TT>) that handles requests, 
and as its second argument, the address of the socket 
in the Internet domain that listens for requests.
This function uses the auxiliary function <TT>domain_of</TT>, 
which extracts the domain of a socket from its address.<BR>
<BR>
In fact, the function <TT>establish_server</TT> is made up 
of high-level functions from the <TT>Unix</TT> library. 
This function sets up a connection to a server.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>establish_server<CODE> </CODE>server_fun<CODE> </CODE>sockaddr<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>domain<CODE> </CODE><CODE>=</CODE><CODE> </CODE>domain_of<CODE> </CODE>sockaddr<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sock<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Unix.socket<CODE> </CODE>domain<CODE> </CODE>Unix<CODE>.</CODE>SOCK_STREAM<CODE> </CODE><CODE>0</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>Unix.bind<CODE> </CODE>sock<CODE> </CODE>sockaddr<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Unix.listen<CODE> </CODE>sock<CODE> </CODE><CODE>3</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><TT>(</TT>s<CODE>,</CODE><CODE> </CODE>caller<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>Unix.accept<CODE> </CODE>sock<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>match</B><CODE> </CODE>Unix.fork()<CODE> </CODE><B>with</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE>-&gt;<CODE> </CODE><B>if</B><CODE> </CODE>Unix.fork()<CODE> </CODE><CODE>&lt;&gt;</CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>then</B><CODE> </CODE>exit<CODE> </CODE><CODE>0</CODE><CODE> </CODE>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>inchan<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Unix.in_channel_of_descr<CODE> </CODE>s<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>outchan<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Unix.out_channel_of_descr<CODE> </CODE>s<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>server_fun<CODE> </CODE>inchan<CODE> </CODE>outchan<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>close_in<CODE> </CODE>inchan<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>close_out<CODE> </CODE>outchan<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>exit<CODE> </CODE><CODE>0</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE>id<CODE> </CODE>-&gt;<CODE> </CODE>Unix.close<CODE> </CODE>s;<CODE> </CODE>ignore<TT>(</TT>Unix.waitpid<CODE> </CODE>[]<CODE> </CODE>id<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><CODE> </CODE>;;<BR><CODE>val establish_server :</CODE><BR><CODE>  (in_channel -&gt; out_channel -&gt; 'a) -&gt; Unix.sockaddr -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>

<A NAME="@fonctions486"></A><BR>
<BR>
To finish building a server with a standalone executable that takes 
a port number parameter, we write a function
<TT>main_server</TT> which takes a parameter indicating a service. 
The function uses the command-line parameter as the port number of
a service. The auxiliary function <TT>get_my_addr</TT>,
returns the address of the local machine. <A NAME="mainserveur"></A>


<PRE><BR># <B>let</B><CODE> </CODE>get_my_addr<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT>Unix.gethostbyname<TT>(</TT>Unix.gethostname()<TT>)</TT><TT>)</TT><CODE>.</CODE>Unix.h_addr_list<CODE>.</CODE><TT>(</TT><CODE>0</CODE><TT>)</TT><CODE> </CODE>;;<BR><CODE>val get_my_addr : unit -&gt; Unix.inet_addr = &lt;fun&gt;</CODE><BR><BR># <B>let</B><CODE> </CODE>main_server<CODE> </CODE><CODE> </CODE>serv_fun<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>Array.length<CODE> </CODE>Sys.argv<CODE> </CODE><CODE>&lt;</CODE><CODE> </CODE><CODE>2</CODE><CODE> </CODE><B>then</B><CODE> </CODE>Printf.eprintf<CODE> </CODE><CODE>"usage : serv_up port\n"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><B>try</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>port<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE>int_of_string<CODE> </CODE>Sys.argv<CODE>.</CODE><TT>(</TT><CODE>1</CODE><TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>my_address<CODE> </CODE><CODE>=</CODE><CODE> </CODE>get_my_addr()<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>establish_server<CODE> </CODE>serv_fun<CODE> </CODE><CODE> </CODE><TT>(</TT>Unix<CODE>.</CODE>ADDR_INET<TT>(</TT>my_address<CODE>,</CODE><CODE> </CODE>port<TT>)</TT><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Failure<TT>(</TT><CODE>"int_of_string"</CODE><TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.eprintf<CODE> </CODE><CODE>"serv_up : bad port number\n"</CODE><CODE> </CODE>;;<BR><CODE>val main_server : (in_channel -&gt; out_channel -&gt; 'a) -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>

<H4> Code for the Service</H4>
The general mechanism is now in place. To illustrate how it works,
we need to define the service we're interested in.
The service here converts strings to upper-case.
It waits for a line of text over an input channel,
converts it, then writes it on the output channel, 
flushing the output buffer. 


<PRE><BR># <B>let</B><CODE> </CODE>uppercase_service<CODE> </CODE>ic<CODE> </CODE>oc<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE>input_line<CODE> </CODE>ic<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>r<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.uppercase<CODE> </CODE>s<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>output_string<CODE> </CODE>oc<CODE> </CODE><TT>(</TT>r<CODE>^</CODE><CODE>"\n"</CODE><TT>)</TT><CODE> </CODE>;<CODE> </CODE>flush<CODE> </CODE>oc<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE><CODE>_</CODE><CODE> </CODE>-&gt;<CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"End of text\n"</CODE><CODE> </CODE>;<CODE> </CODE>flush<CODE> </CODE>stdout<CODE> </CODE>;<CODE> </CODE>exit<CODE> </CODE><CODE>0</CODE><CODE> </CODE>;;<BR><CODE>val uppercase_service : in_channel -&gt; out_channel -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
In order to correctly recover from exceptions
raised in the <TT>Unix</TT> library, 
we wrap the initial call to the service 
in an <I>ad hoc</I> function from the <TT>Unix</TT> library:


<PRE><BR># <B>let</B><CODE> </CODE>go_uppercase_service<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Unix.handle_unix_error<CODE> </CODE>main_server<CODE> </CODE>uppercase_service<CODE> </CODE>;;<BR><CODE>val go_uppercase_service : unit -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>

<H4> Compilation and Testing of the Service</H4>
We group the functions in the file <TT>serv_up.ml</TT>, 
adding an actual call to the function <TT>go_uppercase_service</TT>. 
We compile this file, indicating that the <TT>Unix</TT> library is 
linked in 
<PRE>
ocamlc -i -custom -o serv_up.exe unix.cma serv_up.ml -cclib -lunix
</PRE>The transcript from this compilation (using the option <TT>-i</TT>) gives: 
<PRE>
val establish_server :
  (in_channel -&gt; out_channel -&gt; 'a) -&gt; Unix.sockaddr -&gt; unit
val main_server : (in_channel -&gt; out_channel -&gt; 'a) -&gt; unit
val uppercase_service : in_channel -&gt; out_channel -&gt; unit
val go_uppercase_service : unit -&gt; unit
</PRE>We launch the server by writing:
<PRE>
serv_up.exe 1400
</PRE>The port chosen here is 1400. Now the machine where
the server was launched will accept connections on this 
port.<BR>
<BR>
<A NAME="toc278"></A>
<H3> Testing with <TT>telnet</TT></H3>We can now begin to test the server by using an existing client 
to send and receive lines of text. The <TT>telnet</TT> utility,
which normally is a client of the <TT>telnetd</TT> service 
on port 23, and used to control a remote connection, 
can be diverted from this role by passing a machine name
and a different port number.
This utility exists on several operating systems. 
To test our server under Unix, we type:
<PRE>
$ telnet boulmich 1400 
Trying 132.227.89.6...
Connected to boulmich.ufr-info-p6.jussieu.fr.
Escape character is '^]'.
</PRE>The IP address for <TT>boulmich</TT> is 132.227.89.6 
and its complete name, which contains its domain name, is
<TT>boulmich.ufr-info-p6.jussieu.fr</TT>. 
The text displayed by <TT>telnet</TT> indicates a successful 
connection to the server. 
The client waits for us to type on the keyboard, sending 
the characters to the server that we have launched on <TT>boulmich</TT> on
port 1400.
It waits for a response from the server and displays:
<PRE>
The little cat is dead.
THE LITTLE CAT IS DEAD.
We obtained the expected result.
WE OBTAINED THE EXPECTED result.
</PRE>The phrases entered by the user are in lower-case and those
sent by the server are in upper-case. This is exactly
the role of this service, to perform this conversion.<BR>
<BR>
To exit from the client, we need to close the window where it was
run, by executing the <TT>kill</TT> command. 
This command will close the client's socket, causing the 
server's socket to close as well. 
When the server displays the message ``<TT>End of text</TT>,'' 
the process associated with the service terminates.<BR>
<BR>
<A NAME="toc279"></A>
<H3> The Client Code</H3> 
While the server is naturally parallel (we would like 
to handle a particular request while accepting others,
up to some limit), the client may or may not be so, according 
to the nature of the application. 
Below we give two versions of the client. Beforehand, we present 
two functions that will be useful for writing these clients.<BR>
<BR>
The function <TT>open_connection</TT> from the <TT>Unix</TT> library 
allows us to obtain a couple of input-output channels for a socket.<BR>
<BR>
The following code is contained in the language distribution.


<PRE><BR># <B>let</B><CODE> </CODE>open_connection<CODE> </CODE>sockaddr<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>domain<CODE> </CODE><CODE>=</CODE><CODE> </CODE>domain_of<CODE> </CODE>sockaddr<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sock<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Unix.socket<CODE> </CODE>domain<CODE> </CODE>Unix<CODE>.</CODE>SOCK_STREAM<CODE> </CODE><CODE>0</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>try</B><CODE> </CODE>Unix.connect<CODE> </CODE>sock<CODE> </CODE>sockaddr<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT>Unix.in_channel_of_descr<CODE> </CODE>sock<CODE> </CODE><CODE>,</CODE><CODE> </CODE>Unix.out_channel_of_descr<CODE> </CODE>sock<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>exn<CODE> </CODE>-&gt;<CODE> </CODE>Unix.close<CODE> </CODE>sock<CODE> </CODE>;<CODE> </CODE>raise<CODE> </CODE>exn<CODE> </CODE>;;<BR><CODE>val open_connection : Unix.sockaddr -&gt; in_channel * out_channel = &lt;fun&gt;</CODE><BR>

</PRE>

<A NAME="@fonctions487"></A><BR>
<BR>
Similarly, the function <TT>shutdown_connection</TT> closes 
down a socket.


<PRE><BR># <B>let</B><CODE> </CODE>shutdown_connection<CODE> </CODE>inchan<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Unix.shutdown<CODE> </CODE><TT>(</TT>Unix.descr_of_in_channel<CODE> </CODE>inchan<TT>)</TT><CODE> </CODE>Unix<CODE>.</CODE>SHUTDOWN_SEND<CODE> </CODE>;;<BR><CODE>val shutdown_connection : in_channel -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>

<A NAME="@fonctions488"></A><BR>
<BR>

<H4> A Sequential Client</H4>
From these functions, we can write the main function 
of a sequential client. This client takes as its argument a 
function for sending requests and receiving responses.
This function analyzes the command line arguments to obtain
connection parameters before actual processing. <A NAME="mainclient"></A>


<PRE><BR># <B>let</B><CODE> </CODE>main_client<CODE> </CODE>client_fun<CODE> </CODE><CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>Array.length<CODE> </CODE>Sys.argv<CODE> </CODE><CODE>&lt;</CODE><CODE> </CODE><CODE>3</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>then</B><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"usage :  client server port\n"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><B>let</B><CODE> </CODE>server<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Sys.argv<CODE>.</CODE><TT>(</TT><CODE>1</CODE><TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>server_addr<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><CODE> </CODE>Unix.inet_addr_of_string<CODE> </CODE>server<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Failure<TT>(</TT><CODE>"inet_addr_of_string"</CODE><TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><CODE> </CODE><TT>(</TT>Unix.gethostbyname<CODE> </CODE>server<TT>)</TT><CODE>.</CODE>Unix.h_addr_list<CODE>.</CODE><TT>(</TT><CODE>0</CODE><TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Not_found<CODE> </CODE>-&gt;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.eprintf<CODE> </CODE><CODE>"%s : Unknown server\n"</CODE><CODE> </CODE>server<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>exit<CODE> </CODE><CODE>2</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>try</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>port<CODE> </CODE><CODE>=</CODE><CODE> </CODE>int_of_string<CODE> </CODE><TT>(</TT>Sys.argv<CODE>.</CODE><TT>(</TT><CODE>2</CODE><TT>)</TT><TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sockaddr<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Unix<CODE>.</CODE>ADDR_INET<TT>(</TT>server_addr<CODE>,</CODE>port<TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ic<CODE>,</CODE>oc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>open_connection<CODE> </CODE>sockaddr<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>client_fun<CODE> </CODE>ic<CODE> </CODE>oc<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>shutdown_connection<CODE> </CODE>ic<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Failure<TT>(</TT><CODE>"int_of_string"</CODE><TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE>Printf.eprintf<CODE> </CODE><CODE>"bad port number"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>exit<CODE> </CODE><CODE>2</CODE><CODE> </CODE>;;<BR><CODE>val main_client : (in_channel -&gt; out_channel -&gt; 'a) -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
All that is left is to write the function for client
processing.


<PRE><BR># <B>let</B><CODE> </CODE>client_fun<CODE> </CODE>ic<CODE> </CODE>oc<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_string<CODE> </CODE><CODE> </CODE><CODE>"Request : "</CODE><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>flush<CODE> </CODE>stdout<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>output_string<CODE> </CODE>oc<CODE> </CODE><TT>(</TT><TT>(</TT>input_line<CODE> </CODE>stdin<TT>)</TT><CODE>^</CODE><CODE>"\n"</CODE><TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>flush<CODE> </CODE>oc<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>r<CODE> </CODE><CODE>=</CODE><CODE> </CODE>input_line<CODE> </CODE>ic<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"Response : %s\n\n"</CODE><CODE> </CODE>r;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>r<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>"END"</CODE><CODE> </CODE><B>then</B><CODE> </CODE><TT>(</TT><CODE> </CODE>shutdown_connection<CODE> </CODE>ic<CODE> </CODE>;<CODE> </CODE>raise<CODE> </CODE>Exit<TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Exit<CODE> </CODE>-&gt;<CODE> </CODE>exit<CODE> </CODE><CODE>0</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE>exn<CODE> </CODE>-&gt;<CODE> </CODE>shutdown_connection<CODE> </CODE>ic<CODE> </CODE>;<CODE> </CODE>raise<CODE> </CODE>exn<CODE> </CODE><CODE> </CODE>;;<BR><CODE>val client_fun : in_channel -&gt; out_channel -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>

The function <TT>client_fun</TT> enters an infinite loop 
which reads from the keyboard, sends a string to the server,
gets back the transformed upper-case string, and displays it.
If the string is <CODE>"END"</CODE>, then the exception <TT>Exit</TT> 
is raised in order to exit the loop. If another exception is
raised, typically if the server has shut down, the function
ceases its calculations.<BR>
<BR>
The client program thus becomes:


<PRE><BR># <B>let</B><CODE> </CODE>go_client<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE>main_client<CODE> </CODE>client_fun<CODE> </CODE>;;<BR><CODE>val go_client : unit -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We place all these functions in a file named
<TT>client_seq.ml</TT>, adding a call to the function 
<TT>go_client</TT>. We compile the file with the following
command line:
<PRE>
ocamlc -i -custom -o client_seq.exe unix.cma client_seq.ml -cclib -lunix
</PRE>We run the client as follows:
<PRE>
$ client_seq.exe boulmich 1400 
Request : The little cat is dead.
Response: THE LITTLE CAT IS DEAD.

Request : We obtained the expected result.
Response: WE OBTAINED THE EXPECTED RESULT.

Request : End
Response: END
</PRE>
<H4> The Parallel Client with <TT>fork</TT></H4>
The parallel client mentioned divides its tasks between 
two processes: one for sending, and the other for receiving. 
The processes share the same socket. The functions associated
with each of the processes are passed to them as parameters.<BR>
<BR>
Here is the modified program:


<PRE><BR># <B>let</B><CODE> </CODE>main_client<CODE> </CODE>client_parent_fun<CODE> </CODE>client_child_fun<CODE> </CODE><CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>Array.length<CODE> </CODE>Sys.argv<CODE> </CODE><CODE>&lt;</CODE><CODE> </CODE><CODE>3</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>then</B><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"usage :  client server port\n"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>server<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Sys.argv<CODE>.</CODE><TT>(</TT><CODE>1</CODE><TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>server_addr<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><CODE> </CODE>Unix.inet_addr_of_string<CODE> </CODE>server<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Failure<TT>(</TT><CODE>"inet_addr_of_string"</CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE><B>try</B><CODE> </CODE><CODE> </CODE><TT>(</TT>Unix.gethostbyname<CODE> </CODE>server<TT>)</TT><CODE>.</CODE>Unix.h_addr_list<CODE>.</CODE><TT>(</TT><CODE>0</CODE><TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Not_found<CODE> </CODE>-&gt;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.eprintf<CODE> </CODE><CODE>"%s : unknown server\n"</CODE><CODE> </CODE>server<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>exit<CODE> </CODE><CODE>2</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>try</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>port<CODE> </CODE><CODE>=</CODE><CODE> </CODE>int_of_string<CODE> </CODE><TT>(</TT>Sys.argv<CODE>.</CODE><TT>(</TT><CODE>2</CODE><TT>)</TT><TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sockaddr<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Unix<CODE>.</CODE>ADDR_INET<TT>(</TT>server_addr<CODE>,</CODE>port<TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ic<CODE>,</CODE>oc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>open_connection<CODE> </CODE>sockaddr<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>match</B><CODE> </CODE>Unix.fork<CODE> </CODE>()<CODE> </CODE><B>with</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE>-&gt;<CODE> </CODE><B>if</B><CODE> </CODE>Unix.fork()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>then</B><CODE> </CODE>client_child_fun<CODE> </CODE>oc<CODE> </CODE>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>exit<CODE> </CODE><CODE>0</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE>id<CODE> </CODE>-&gt;<CODE> </CODE>client_parent_fun<CODE> </CODE>ic<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>shutdown_connection<CODE> </CODE>ic<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>Unix.waitpid<CODE> </CODE>[]<CODE> </CODE>id<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Failure<TT>(</TT><CODE>"int_of_string"</CODE><TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE>Printf.eprintf<CODE> </CODE><CODE>"bad port number"</CODE><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>exit<CODE> </CODE><CODE>2</CODE><CODE> </CODE>;;<BR><CODE>val main_client : (in_channel -&gt; 'a) -&gt; (out_channel -&gt; unit) -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>

The expected behavior of the parameters is: the (grand)child sends
the request and the parent receives the response.<BR>
<BR>
This architecture has the effect that if the child needs to send 
several requests, then the parent receives the responses 
to requests as each is processed. Consider again the preceding 
example for capitalizing strings, modifying the client side program.
The client reads the text from one file, while writing 
the response to another file. For this we need a function 
that copies from one channel, <TT>ic</TT>, to another, <TT>oc</TT>, 
respecting our little protocol (that is, it recognizes the 
string <CODE>"END"</CODE>).


<PRE><BR># <B>let</B><CODE> </CODE>copy_channels<CODE> </CODE>ic<CODE> </CODE>oc<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE>input_line<CODE> </CODE>ic<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>if</B><CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>"END"</CODE><CODE> </CODE><B>then</B><CODE> </CODE>raise<CODE> </CODE>End_of_file<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><TT>(</TT>output_string<CODE> </CODE>oc<CODE> </CODE><TT>(</TT>s<CODE>^</CODE><CODE>"\n"</CODE><TT>)</TT>;<CODE> </CODE>flush<CODE> </CODE>oc<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>End_of_file<CODE> </CODE>-&gt;<CODE> </CODE>()<CODE> </CODE>;;<BR><CODE>val copy_channels : in_channel -&gt; out_channel -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We write the two functions for the child and parent 
using the parallel client model:


<PRE><BR># <B>let</B><CODE> </CODE>child_fun<CODE> </CODE>in_file<CODE> </CODE>out_sock<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>copy_channels<CODE> </CODE>in_file<CODE> </CODE>out_sock<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>output_string<CODE> </CODE>out_sock<CODE> </CODE><TT>(</TT><CODE>"FIN\n"</CODE><TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>flush<CODE> </CODE>out_sock<CODE> </CODE>;;<BR><CODE>val child_fun : in_channel -&gt; out_channel -&gt; unit = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>parent_fun<CODE> </CODE>out_file<CODE> </CODE>in_sock<CODE> </CODE><CODE>=</CODE><CODE> </CODE>copy_channels<CODE> </CODE>in_sock<CODE> </CODE>out_file<CODE> </CODE>;;<BR><CODE>val parent_fun : out_channel -&gt; in_channel -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
Now we can write the main client function. It must 
collect two extra command line parameters:
the names of the input and output files.


<PRE><BR># <B>let</B><CODE> </CODE>go_client<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>Array.length<CODE> </CODE>Sys.argv<CODE> </CODE><CODE>&lt;</CODE><CODE> </CODE><CODE>5</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>then</B><CODE> </CODE>Printf.eprintf<CODE> </CODE><CODE>"usage : client_par server port filein fileout\n"</CODE><BR><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><B>let</B><CODE> </CODE>in_file<CODE> </CODE><CODE>=</CODE><CODE> </CODE>open_in<CODE> </CODE>Sys.argv<CODE>.</CODE><TT>(</TT><CODE>3</CODE><TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>out_file<CODE> </CODE><CODE>=</CODE><CODE> </CODE>open_out<CODE> </CODE>Sys.argv<CODE>.</CODE><TT>(</TT><CODE>4</CODE><TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>main_client<CODE> </CODE><TT>(</TT>parent_fun<CODE> </CODE>out_file<TT>)</TT><CODE> </CODE><TT>(</TT>child_fun<CODE> </CODE>in_file<TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>close_in<CODE> </CODE>in_file<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>close_out<CODE> </CODE>out_file<CODE> </CODE>;;<BR><CODE>val go_client : unit -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We gather all of our material into the file <TT>client_par.ml</TT>
(making sure to include a call to <TT>go_client</TT>), and compile it. 
We create a file <TT>toto.txt</TT> containing the text to be converted:
<PRE>
The little cat is dead.
We obtained the expected result.
</PRE>We can test the client by typing:
<PRE>
client_par.exe boulmich 1400 toto.txt result.txt
</PRE>The file <TT>result.txt</TT> contains the text: 
<PRE>
$ more result.txt
THE LITTLE CAT IS DEAD.
WE OBTAINED THE EXPECTED RESULT.
</PRE>When the client finishes, the server always displays the message
<CODE>"End of text"</CODE>.<BR>
<BR>
<A NAME="toc280"></A>
<H3> Client-server Programming with Lightweight Processes</H3>
The preceding presentation of code for a generic server and
a parallel client created processes via the <TT>fork</TT> primitive
in the <TT>Unix</TT> library. This works well under Unix; many
Unix services are implemented by this technique. 
Unfortunately, the same cannot be said for Windows.
For portability, it is preferable to write client-server
code with lightweight processes, which were presented in 
Chapter&nbsp;<A HREF="index.html#chap-PC">19</A>. In this case, it becomes necessary 
to examine the interactions among different server processes.<BR>
<BR>

<H4> Threads and the <TT>Unix</TT> Library</H4>
The simultaneous use of lightweight processes and the <TT>Unix</TT>
library causes all active threads to block if a system call does 
not return immediately. In particular, reads on file descriptors, 
including those created by <TT>socket</TT>, are blocking.<BR>
<BR>
To avoid this problem, the <TT>ThreadUnix</TT> library 
reimplements most of the input-output functions from the 
<TT>Unix</TT> library. The functions defined in that library will only 
block the thread which is actually making the system call. 
As a consequence, input and output is handled with the 
low-level functions <TT>read</TT> and <TT>write</TT> found 
in the <TT>ThreadUnix</TT> library.
<BR>
<BR>
<A NAME="@fonctions489"></A>
For example, the standard function for reading a string of characters,
<TT>input_line</TT>, is redefined in such a way that it does not 
block other threads while reading a line.


<PRE><BR># <B>let</B><CODE> </CODE>my_input_line<CODE> </CODE><CODE> </CODE>fd<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>" "</CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE><CODE> </CODE>r<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ref<CODE> </CODE><CODE>""</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>while</B><CODE> </CODE><TT>(</TT>ThreadUnix.read<CODE> </CODE>fd<CODE> </CODE>s<CODE> </CODE><CODE>0</CODE><CODE> </CODE><CODE>1</CODE><CODE> </CODE><CODE>&gt;</CODE><CODE> </CODE><CODE>0</CODE><TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE>s<CODE>.[</CODE><CODE>0</CODE><CODE>]</CODE><CODE> </CODE><CODE>&lt;&gt;</CODE><CODE> </CODE><CODE>'\n'</CODE><CODE> </CODE><B>do</B><CODE> </CODE>r<CODE> </CODE><CODE>:=</CODE><CODE> </CODE><CODE>!</CODE>r<CODE> </CODE><CODE>^</CODE>s<CODE> </CODE><B>done</B><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>!</CODE>r<CODE> </CODE>;;<BR><CODE>val my_input_line : Unix.file_descr -&gt; string = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>

<H4> Classes for a Server with Threads</H4>
Now let us recycle the example of the CAPITALIZATION service, this 
time giving a version using lightweight processes. Shifting
to threads poses no problem for our little application
on either the server side or the client side, which start 
processes independently.<BR>
<BR>
Earlier, we built a generic server parameterized over a service
function. We were able to achieve this kind of abstraction by relying on 
the functional aspect of the Objective CAML language. Now we are about to
use the object-oriented extensions to the language to show how objects 
allow us to achieve a comparable abstraction.<BR>
<BR>
The server is organized into two classes: 
<TT>serv_socket</TT> and <TT>connection</TT>. The first of these
handles the service startup, and the second, the service itself. 
We have introduced some print statements to trace the main 
stages of the service.<BR>
<BR>

<H5> The <TT>serv_socket</TT> class.</H5> has two instance variables: 
<TT>port</TT>, the port number for the service, and <TT>socket</TT>, 
the socket for listening. When constructing such an object, the initializer 
opens the service and creates this socket. The <TT>run</TT> method 
accepts connections and creates a new <TT>connection</TT> object for handling 
requests. The <I>serv_socket</I> uses the <I>connection</I> class described 
in the following paragraph. Usually, this class must be defined before the
<I>serv_socket</I> class.<BR>
<BR>


<PRE><BR># <B>class</B><CODE> </CODE>serv_socket<CODE> </CODE>p<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>object</B><CODE> </CODE><TT>(</TT>self<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE>port<CODE> </CODE><CODE>=</CODE><CODE> </CODE>p<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE><B>mutable</B><CODE> </CODE>sock<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ThreadUnix.socket<CODE> </CODE>Unix<CODE>.</CODE>PF_INET<CODE> </CODE>Unix<CODE>.</CODE>SOCK_STREAM<CODE> </CODE><CODE>0</CODE><BR><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>initializer</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>my_address<CODE> </CODE><CODE>=</CODE><CODE> </CODE>get_my_addr<CODE> </CODE>()<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>Unix.bind<CODE> </CODE>sock<CODE> </CODE><TT>(</TT>Unix<CODE>.</CODE>ADDR_INET<TT>(</TT>my_address<CODE>,</CODE>port<TT>)</TT><TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Unix.listen<CODE> </CODE>sock<CODE> </CODE><CODE>3</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE><B>private</B><CODE> </CODE>client_addr<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>function</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Unix<CODE>.</CODE>ADDR_INET<TT>(</TT>host<CODE>,_</CODE><TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE>Unix.string_of_inet_addr<CODE> </CODE>host<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE>-&gt;<CODE> </CODE><CODE>"Unexpected client"</CODE><BR><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE>run<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><TT>(</TT><B>true</B><TT>)</TT><CODE> </CODE><B>do</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><TT>(</TT>sd<CODE>,</CODE>sa<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>ThreadUnix.accept<CODE> </CODE>sock<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>connection<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>new</B><CODE> </CODE>connection<TT>(</TT>sd<CODE>,</CODE>sa<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"TRACE.serv: new connection from %s\n\n"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT>self#client_addr<CODE> </CODE>sa<TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>connection#start<CODE> </CODE>()<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><CODE> </CODE>;;<BR><CODE>class serv_socket :</CODE><BR><CODE>  int -&gt;</CODE><BR><CODE>  object</CODE><BR><CODE>    val port : int</CODE><BR><CODE>    val mutable sock : Unix.file_descr</CODE><BR><CODE>    method private client_addr : Unix.sockaddr -&gt; string</CODE><BR><CODE>    method run : unit -&gt; unit</CODE><BR><CODE>  end</CODE><BR>

</PRE>

It is possible to refine the server by inheriting from this class and redefining 
the <TT>run</TT> method.<BR>
<BR>

<H5> The <TT>connection</TT> class.</H5>
The instance variables in this class, 
<TT>s_descr</TT> and <TT>s_addr</TT>, are 
initialized to the descriptor and the address of the socket created
by <TT>accept</TT>. The methods are <TT>start</TT>, <TT>run</TT>,
and <TT>stop</TT>. The <TT>start</TT> creates a thread calling the
two other methods, and returns its thread identifier, which can be 
used by the calling instance of <TT>serv_socket</TT>.
The <TT>run</TT> method contains the core functionality of the 
service. We have slightly modified the termination condition for 
the service: we exit on receipt of an empty string.
The <TT>stop</TT> service just closes the socket descriptor 
for the service. <BR>
<BR>
Each new connection has an associated number obtained by 
calling the auxiliary function <TT>gen_num</TT> when 
the instance is created.
<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>gen_num<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>let</B><CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ref<CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>in</B><CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>()<CODE> </CODE>-&gt;<CODE> </CODE>incr<CODE> </CODE>c;<CODE> </CODE><CODE>!</CODE>c<TT>)</TT><CODE> </CODE>;;<BR><CODE>val gen_num : unit -&gt; int = &lt;fun&gt;</CODE><BR># <B>exception</B><CODE> </CODE>Done<CODE> </CODE>;;<BR><CODE>exception Done</CODE><BR># <B>class</B><CODE> </CODE>connection<CODE> </CODE><TT>(</TT>sd<CODE>,</CODE>sa<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>object</B><CODE> </CODE><TT>(</TT>self<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE>s_descr<CODE> </CODE><CODE>=</CODE><CODE> </CODE>sd<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE>s_addr<CODE> </CODE><CODE>=</CODE><CODE> </CODE>sa<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE><B>mutable</B><CODE> </CODE>number<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>0</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>initializer</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>number<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>gen_num();<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"TRACE.connection : object %d created\n"</CODE><CODE> </CODE>number<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_newline()<BR><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE>start<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE>Thread.create<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>x<CODE> </CODE>-&gt;<CODE> </CODE>self#run<CODE> </CODE>x<CODE> </CODE>;<CODE> </CODE>self#stop<CODE> </CODE>x<TT>)</TT><CODE> </CODE>()<BR><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE>stop()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Printf.printf<CODE> </CODE><CODE>"TRACE.connection : object finished %d\n"</CODE><CODE> </CODE>number<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_newline<CODE> </CODE>()<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Unix.close<CODE> </CODE>s_descr<BR><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE>run<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>line<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE>my_input_line<CODE> </CODE>s_descr<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>if</B><CODE> </CODE><TT>(</TT>line<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>""</CODE><TT>)</TT><CODE> </CODE><B>or</B><CODE> </CODE><TT>(</TT>line<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>"\013"</CODE><TT>)</TT><CODE> </CODE><B>then</B><CODE> </CODE>raise<CODE> </CODE>Done<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>result<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>String.uppercase<CODE> </CODE>line<TT>)</TT><CODE>^</CODE><CODE>"\n"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>ThreadUnix.write<CODE> </CODE>s_descr<CODE> </CODE>result<CODE> </CODE><CODE>0</CODE><CODE> </CODE><TT>(</TT>String.length<CODE> </CODE>result<TT>)</TT><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Done<CODE> </CODE>-&gt;<CODE> </CODE>()<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE>exn<CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE>print_string<CODE> </CODE><TT>(</TT>Printexc.to_string<CODE> </CODE>exn<TT>)</TT><CODE> </CODE>;<CODE> </CODE>print_newline()<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><CODE> </CODE>;;<BR><CODE>class connection :</CODE><BR><CODE>  Unix.file_descr * 'a -&gt;</CODE><BR><CODE>  object</CODE><BR><CODE>    val mutable number : int</CODE><BR><CODE>    val s_addr : 'a</CODE><BR><CODE>    val s_descr : Unix.file_descr</CODE><BR><CODE>    method run : unit -&gt; unit</CODE><BR><CODE>    method start : unit -&gt; Thread.t</CODE><BR><CODE>    method stop : unit -&gt; unit</CODE><BR><CODE>  end</CODE><BR>

</PRE>
<BR>
<BR>
Here again, by inheritance and redefinition of the <TT>run</TT> method,
we can define a new service.<BR>
<BR>
We can test this new version of the server by running the 
<TT>protect_serv</TT> function.


<PRE><BR># <B>let</B><CODE> </CODE>go_serv<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>let</B><CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>new</B><CODE> </CODE>serv_socket<CODE> </CODE><CODE>1</CODE><CODE>4</CODE><CODE>0</CODE><CODE>0</CODE><CODE> </CODE><B>in</B><CODE> </CODE>s#run<CODE> </CODE>()<CODE> </CODE>;;<BR># <B>let</B><CODE> </CODE>protect_serv<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Unix.handle_unix_error<CODE> </CODE>go_serv<CODE> </CODE>()<CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
<A NAME="toc281"></A>
<H3> Multi-tier Client-server Programming</H3>
Even though the client-server relation is asymmetric, nothing prevents 
a server from being the client of another service.
In this way, we have a communication hierarchy. 
A typical client-server application might be the following:
<BR>
<BR>
<UL>
<LI>
 a mail client presents a friendly user interface;

<LI> a word-processing program is run, followed by an interaction with the user;

<LI> the word-processing program accesses a database.
</UL>One of the goals of client-server applications is to alleviate the processing 
of centralized machines. Figure&nbsp;<A HREF="book-ora187.html#fig-pdtiers">20.3</A> shows two client-server 
architectures with three tiers.<BR>
<BR>
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora082.gif">
</DIV>
<BR>
<DIV ALIGN=center>Figure 20.3: Different client-server architectures</DIV><BR>

<A NAME="fig-pdtiers"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE>Each tier may run on a different machine. 
The user interface runs on the machine running the user mail application.
The processing part is handled by a machine shared by a collection
of users, which itself sends requests to a remote database server.
With this application, a particular piece of 
data may be sent to the user mail application or to the database server.<BR>
<BR>
<A NAME="toc282"></A>
<H3> Some Remarks on the Client-server Programs</H3>In the preceding sections, we constructed servers for a simple CAPITALIZATION 
service. Each server used a different approach for its implementation.
The first such server used the Unix fork mechanism. Once we
built that server, it became possible to test it with the telnet 
client supplied with the Unix, Windows, and MacOS operating 
systems. Next, we built a simple first client. We were then able 
to test the client and server together. Clients may have tasks 
to manage between communications. For this purpose, we built the 
<TT>client_par.exe</TT> client, which separates reading from writing 
by using forks. A new kind of server was built using threads
to clearly show the relative independence of the server and the client, 
and to bring input-output into this setting. This server was 
organized into two easily-reused classes. We note that both functional
programming and object-oriented programming support the separation
of ``mechanical,'' reusable code from code for specialized processing.<BR>
<BR>
<HR>
<A HREF="book-ora186.html"><IMG SRC ="previous_motif.gif" ALT="Previous"></A>
<A HREF="index.html"><IMG SRC ="contents_motif.gif" ALT="Contents"></A>
<A HREF="book-ora188.html"><IMG SRC ="next_motif.gif" ALT="Next"></A>
</BODY>
</HTML>
